今天回來Client應用方這裡, 就是應用程式端這裡聊Instrumentation libraries.
畢竟規範了這麼多Model, Interface, Layer design, 就是希望能讓各開發者們可以加入開發其周遭.
OTel其實提供了很多Automatic Instrumentation libraries.
這些Automatic Instrumentation要根據各語言以及框架來選用添加的.
讓我們開發者在使用OpenTelemetry上更是方便,
不寫任何程式碼或幾乎少少的程式碼設定, 就可以達到所需要的三本柱相關的效果.
像.Net有 System.Diagnostics.Activity內建來產生trace或一些監控參考.
Azure與OTel相關文章能參考此處Azure Monitor
但是像Go是沒有支援自動檢測的功能, 就需要依賴於特定的instrumentation libraries生成telemetry data.
想要什麼指標或類型的telemetry data由開發人員決定合理的檢測範圍.
以下列舉一些OTel內建的GO instrumentation libraries以及提供的telemetry data範圍.
| Instrumentation Package | Metrics | Traces |
|---|---|---|
| github.com/astaxie/beego | ✓ | ✓ |
| github.com/aws/aws-sdk-go-v2 | ✓ | |
| github.com/bradfitz/gomemcache | ✓ | |
| github.com/emicklei/go-restful | ✓ | |
| github.com/gin-gonic/gin | ✓ | |
| github.com/go-kit/kit | ✓ | |
| github.com/gocql/gocql | ✓ | ✓ |
| github.com/gorilla/mux | ✓ | |
| github.com/labstack/echo | ✓ | |
| github.com/Shopify/sarama | ✓ | |
| go.mongodb.org/mongo-driver | ✓ | |
| google.golang.org/grpc) | ✓ | |
| gopkg.in/macaron.v1 | ✓ | |
| host | ✓ | |
| net/http | ✓ | ✓ |
| net/http/httptrace | ✓ | |
| runtime | ✓ | |
| github.com/XSAM/otelsql | ✓ | ✓ |
挑otelsql來玩看看
docker-compose.yml
version: "3.7"
services:
mysql:
image: mysql:latest
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=otel_password
- MYSQL_DATABASE=db
client:
build:
dockerfile: $PWD/Dockerfile
context: ..
ports:
- 2222:2222
depends_on:
- mysql
- otel-collector
# Jaeger
jaeger-all-in-one:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686"
- "14268"
- "14250"
- "6831:6831/udp"
otel-collector:
image: otel/opentelemetry-collector-contrib-dev:latest
command: ["--config=/etc/otel-collector-config.yaml", ""]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "1888:1888" # pprof extension
- "8888:8888" # Prometheus metrics exposed by the collector
- "8889:8889" # Prometheus exporter metrics
- "13133:13133" # health_check extension
- "4317:4317" # OTLP gRPC receiver
- "55679:55679" # zpages extension
- "14268:14268"
depends_on:
- jaeger-all-in-one
prometheus:
container_name: prometheus
image: prom/prometheus:latest
volumes:
- ./prometheus.yaml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
otel-collector-config.yaml
這次使用了prometheus_simple, 來抓取target的metric data
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
prometheus_simple:
collection_interval: 10s
endpoint: "client:2222"
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
const_labels:
label1: value1
logging:
zipkin:
endpoint: "http://zipkin-all-in-one:9411/api/v2/spans"
format: proto
jaeger:
endpoint: jaeger-all-in-one:14250
tls:
insecure: true
processors:
batch:
extensions:
health_check:
pprof:
endpoint: :1888
zpages:
endpoint: :55679
service:
extensions: [pprof, zpages, health_check]
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [logging, zipkin, jaeger]
metrics:
receivers: [otlp, prometheus_simple]
processors: [batch]
exporters: [logging, prometheus]
prometheus.yaml
這個job, 是抓取OTel collector的metrics
scrape_configs:
- job_name: 'otel-collector'
scrape_interval: 10s
static_configs:
- targets: ['otel-collector:8889']
- targets: ['otel-collector:8888']
package main
import (
"context"
"database/sql"
"fmt"
"log"
"net/http"
"time"
"github.com/XSAM/otelsql"
_ "github.com/go-sql-driver/mysql"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric/global"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
selector "go.opentelemetry.io/otel/sdk/metric/selector/simple"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const instrumentationName = "github.com/XSAM/otelsql/example"
var serviceName = semconv.ServiceNameKey.String("otesql-example")
var mysqlDSN = "root:otel_password@tcp(mysql)/db?parseTime=true"
func initTracer() {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx, "otel-collector:4317", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
if err != nil {
log.Fatal(err)
}
traceExporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn))
if err != nil {
log.Fatal(err)
}
bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithSpanProcessor(bsp),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
serviceName,
)),
)
otel.SetTracerProvider(tp)
}
func initMeter() {
c := controller.New(
processor.NewFactory(
selector.NewWithHistogramDistribution(),
aggregation.CumulativeTemporalitySelector(),
processor.WithMemory(true),
),
controller.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
serviceName,
)),
)
metricExporter, err := prometheus.New(prometheus.Config{}, c)
if err != nil {
log.Fatalf("failed to install metric exporter, %v", err)
}
global.SetMeterProvider(metricExporter.MeterProvider())
http.HandleFunc("/", metricExporter.ServeHTTP)
go func() {
_ = http.ListenAndServe(":2222", nil)
}()
fmt.Println("Prometheus server running on :2222")
}
func main() {
initTracer()
initMeter()
// Connect to database
db, err := otelsql.Open("mysql", mysqlDSN, otelsql.WithAttributes(
semconv.DBSystemMySQL,
))
if err != nil {
panic(err)
}
defer db.Close()
err = otelsql.RegisterDBStatsMetrics(db, otelsql.WithAttributes(
semconv.DBSystemMySQL,
))
if err != nil {
panic(err)
}
err = run(db)
if err != nil {
panic(err)
}
fmt.Println("Example finished updating, please visit :2222")
select {}
}
func run(db *sql.DB) error {
// Create a parent span (Optional)
tracer := otel.GetTracerProvider()
ctx, span := tracer.Tracer(instrumentationName).Start(context.Background(), "example")
defer span.End()
err := query(ctx, db)
if err != nil {
span.RecordError(err)
return err
}
return nil
}
func query(ctx context.Context, db *sql.DB) error {
// Make a query
rows, err := db.QueryContext(ctx, `SELECT CURRENT_TIMESTAMP`)
if err != nil {
return err
}
defer rows.Close()
var currentTime time.Time
for rows.Next() {
err = rows.Scan(¤tTime)
if err != nil {
return err
}
}
fmt.Println(currentTime)
return nil
}
首先執行
docker-compose up -d
稍待一會後執行client來看看
docker-compose up client

一樣先看看OTel collector的pipeline配置了什麼
http://localhost:55679/debug/pipelinez
然後打開Prometheus UI http://localhost:9090/
可以看到我們在resource context給的service name有出來,
且有如期收到該instrumentation library所吐出的metrics data
最後看看Jaeger UI
http://localhost:16686/

以下就是這次Trace底下的所有Span
Automatic Instrumentation真的方便XD
在公司寫.NetCore專案時, 一些Automatic Instrumentation加入專案後啟用, 很多資訊都被捕捉出來了, 像這次的sql span也有.
這次展示的otelsql, 雖然span的範圍要自己start, 但metric則是設定好後就自己去處理了. 挺方便
還是要看各語言或框架的生態圈怎麼去擴展了.
OpenTelemetry的部份暫時先介紹到這.
希望未來的工作機會上, 有可能把這些可觀測性的價值與文化, 帶入公司與團隊.
讓Dev與Ops能相互合作, 相互味彼此提供價值.
希望能加入這樣的團隊XD
未來也可能在以這主題, 聊的更深, 結合的更廣.
下面提供一些不錯的連結供學習參考
awesome-opentelemetry
UpTrace
Aspecto
signoz
A beginner’s guide to OpenTelemetry